//
//  LDBLEManager.m
//  APP2PDemo
//
//  Created by michael on 2023/4/17.
//

#import "APBLEManager.h"
#import <pthread.h>
#import "AYBluetooth.h"
#import "VLTimer.h"
#import "APBLEDevice.h"
#import "NSData+VLExtension.h"
#import "FWLogger.h"
/*
 蓝牙名称前缀：
 
 
 
 VLink数据帧结构
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 |                  1Byte                 |                 2Byte                  |                3Byte                   |                   4Byte                |
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 |                                       sync_code                                 |                                         len                                     |
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 |                 dst_addr               |               src_addr                 |                type                    |                   data
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|                                               ...                                       |                                      check_sum                                  |
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 注：
 sync_code为起始码：固定值为A5F5
 len为数据帧长度为从dst_addr（包含）到check_sum（包含）的长度，数据
 dst_addr为目标地址，参见下面XXX_ADDR
 src_addr为源地址，参见下面XXX_ADDR
 type为协议类型：值参见DV_VL_MSG，目前只用这一个类型
 data为VLink协议消息
 check_sum为校验码 CRC16(len + dst_addr + src_addr +type +data)（crc16唯一恶搞校验算法）
 
 
 
 VLink协议消息,其数据结构如下
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 |                  1Byte                 |                 2Byte                  |                3Byte                   |                   4Byte                |
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 |      ver(4bit)    |type(2bit)|                     seq(10bit)                   |                                   cmd(16bit)                                    |
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 |                                                                            data（可变长度）                                                                         |
 |----------------------------------------|----------------------------------------|----------------------------------------|----------------------------------------|
 
 注：
 ver为消息协议版本号，目前为1，占4bit
 type为消息类型：NON（0，不需要应答的数据）、CON（1，必须要应答的数据）、
 ACK（3， 针对某个 CON 的应答数据）占2bit
 seq为消息序号，用于匹配应答，应答包 seq 需和请求包 seq 一致。占10bit
 cmd为消息命令，比如配网命令为0x9001，占16bit
 data：为消息载荷，长度不定
 参考下面的LDVLinkMsgHeader数据结构
 
 */


//WiFi模组地址
static const uint8_t WFMD_ADDR = 0x02;
//锁端地址
static const uint8_t LKMD_ADDR = 0x03;
//APP端地址
static const uint8_t APP_ADDR = 0x04;
//测试端地址
static const uint8_t DVTEST_ADDR = 0x80;

//VLink.type 协议消息数据
static const uint8_t DV_VL_MSG = 0x01;
//VLink.type 360SDK数据(用不到)
static const uint8_t DV_360_MSG = 0x80;

///< 掩码
static const uint16_t seq_mask = 0x03FF;
static const uint16_t type_mask = 0x0C00;
static const uint16_t ver_mask = 0xF000;
#pragma pack(1)
typedef struct {
    uint16_t data;
    uint16_t cmd;
}LDVLinkMsgHeader;
#pragma pack()

@implementation APBLEResponse

+ (instancetype)responseWithCommand:(APBLEVLinkCommandType)command isTimeout:(BOOL)isTimeout data:(NSData *)data {
    APBLEResponse *resp = [[self alloc] init];
    resp.command = command;
    resp.isTimeout = isTimeout;
    resp.data = data;
    return resp;
}

@end
@interface APBLEManager()

@property (nonatomic, strong) AYBluetooth *bluetooth;
@property (nonatomic, strong) CBCharacteristic *notifyCharc; ///< 读取特征
@property (nonatomic, strong) CBCharacteristic *writeCharc; ///< 写入特征
@property (nonatomic, strong, nullable) APBLEDevice *device;
@property (nonatomic, assign) uint16_t packetDataLength;
@property (nonatomic, strong) NSMutableData *packetData;
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, VLTimer *> *commandTimers;
@property (nonatomic, strong) NSMutableDictionary *commandResponses;
@property (nonatomic, strong) NSMutableDictionary *commandSequences;
@property (nonatomic, copy, nullable) NSString *bleName;
@property (nonatomic, copy, readonly) NSString *readUUID;
@property (nonatomic, copy, readonly) NSString *writeUUID;
@property (nonatomic, assign, readonly) BOOL shouldEncrypt; ///< 是否需要加密
@property (nonatomic, assign, readonly) BOOL needEncrypt;   ///< 是否需要加密
@property (nonatomic, assign, readonly) int vl_diff_len; ///< VLink协议不同部分数据长度
@property (nonatomic, assign, readonly) uint32_t vl_diff_val; ///< VLink协议不同部分数据值
@property (nonatomic, assign, ) NSUInteger randomCode; ///< 随机码（通讯加密用）
@property (nonatomic, assign) uint16_t syncCode;
@property (nonatomic, assign) APBLEVLinkCommandType sendedCommand;
@property (nonatomic, copy) NSDictionary *config;
@property (nonatomic, copy) NSArray<NSString *> *prefixes;
@property (nonatomic, assign) BOOL isConnecting;
@property (nonatomic, assign) BOOL isCommandRepared;
@end

@implementation APBLEManager

- (NSString *)readUUID {
    NSString *uuid = [[self.config[@"read_uuid"] componentsSeparatedByString:@"-"] firstObject];
    if (uuid.length < 4) return @"FFF6";
    return [uuid substringFromIndex:uuid.length - 4].uppercaseString;
}

- (NSString *)writeUUID {
    NSString *uuid = [[self.config[@"write_uuid"] componentsSeparatedByString:@"-"] firstObject];
    if (uuid.length < 4) return @"FFF6";
    return [uuid substringFromIndex:uuid.length - 4].uppercaseString;
}

///< 协议内容必须加密
- (BOOL)needEncrypt {
    return self.shouldEncrypt && self.encryptKey;
}
///< 协议内容应该加密，可能由于未获取到密钥，实际通讯不一定会加密
- (BOOL)shouldEncrypt {
    return [self.config[@"need_encrypt"] boolValue];
}

- (uint16_t)syncCode {
    NSString *sync_code = self.config[@"sync_code"];
    if (sync_code.length != 4) return 0xF5A5;
    return sync_code.vl_hexData.vl_uint16;
}

- (int)vl_diff_len {
    if (self.shouldEncrypt) return sizeof(uint32_t);
    return sizeof(uint8_t);
}

- (uint32_t)vl_diff_val {
    if (self.shouldEncrypt) return (uint32_t)self.randomCode?:1;
    return DV_VL_MSG;
}


- (CBManagerState)bleState {
    return _bluetooth.centralManager.state;
}

- (BOOL)isConnected {
    return self.device.peripheral.state == CBPeripheralStateConnected;
}

+ (instancetype)sharedManager {
    static dispatch_once_t onceToken;
    static APBLEManager *_instance;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}


- (instancetype)init
{
    self = [super init];
    if (self) {
        _bluetooth = [AYBluetooth sharedBluetooth];
        _packetData = [NSMutableData data];
        _commandResponses = [NSMutableDictionary dictionary];
        _commandTimers = [NSMutableDictionary dictionary];
        _commandSequences = [NSMutableDictionary dictionary];
        _randomCode = 0;
        _packetDataLength = 0;
        [self initDelegate];
    }
    return self;
}

- (void)initDelegate {
    __weak typeof(self) weakSelf = self;
    [_bluetooth setBlockOnCentralManagerDidUpdateState:^(CBCentralManager *central) {
        DDLogInfo(@"🔵: ble update state:%zd",central.state);
        [[NSNotificationCenter defaultCenter] postNotificationName:bleCentralStateDidUpdateNotification object:@(central.state)];
    }];
    //设备名称过滤器
    [_bluetooth setFilterOnDiscoverPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) {
        return [weakSelf.prefixes indexOfObjectPassingTest:^BOOL(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            return [peripheralName hasPrefix:obj];
        }] != NSNotFound && advertisementData[CBAdvertisementDataManufacturerDataKey];
    }];
    [_bluetooth setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) { // 扫描到设备的委托
        APBLEDevice *device = [[APBLEDevice alloc] initWithAdvertisementData:advertisementData peripheral:peripheral prefixes:weakSelf.prefixes];
        DDLogInfo(@"🔵: discover peripheral:%@",peripheral);
        [[NSNotificationCenter defaultCenter] postNotificationName:blePeripheralDiscoverNotification object:device userInfo:nil];
        NSString *bleMac = [weakSelf.bleName stringByReplacingOccurrencesOfString:weakSelf.prefixes.firstObject withString:@""].lowercaseString;
        if (weakSelf.bleName && [device.mac isEqualToString:bleMac]) {
            weakSelf.bleName = nil;
            weakSelf.device = device;
            [weakSelf connectPeripheral:peripheral]; // 连接外设
        }
    }];
    [_bluetooth setBlockOnCancelAllPeripheralsConnectionBlock:^(CBCentralManager *centralManager) {
        NSLog(@"setBlockOnCancelAllPeripheralsConnectionBlock");
    }];
    [_bluetooth setBlockOnCancelScanBlock:^(CBCentralManager *centralManager) {
        
    }];
    [_bluetooth setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) { // 连接外设 成功
        DDLogInfo(@"🔵: connect success %@",[weakSelf.device mac]);
        weakSelf.isConnecting = NO;
        [weakSelf fulfillCommandResponse:[APBLEResponse responseWithCommand:APBLEVLinkCommandType_ConnnectState isTimeout:NO data:VLInt8Data(APBLEDeviceConnectStatus_Success)]];
        if (weakSelf.shouldEncrypt) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [weakSelf sendCommand:APBLEVLinkCommandType_RandomCode data:nil timeout:2 completion:nil];
            });
        }else {
            [weakSelf fulfillCommandResponse:[APBLEResponse responseWithCommand:APBLEVLinkCommandType_RandomCode isTimeout:NO data:VLInt8Data(APBLEDeviceConnectStatus_Success)]];
        }
    }];
    [_bluetooth setBlockOnFailToConnect:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        DDLogInfo(@"🔵: connect failed %@",error.description);
        weakSelf.device = nil;
        [weakSelf fulfillCommandResponse:[APBLEResponse responseWithCommand:APBLEVLinkCommandType_ConnnectState isTimeout:NO data:VLInt8Data(APBLEDeviceConnectStatus_Failure)]];
        [weakSelf fulfillCommandResponse:[APBLEResponse responseWithCommand:APBLEVLinkCommandType_RandomCode isTimeout:YES data:VLInt8Data(APBLEDeviceConnectStatus_Success)]];
    }];
    [_bluetooth setBlockOnDisconnect:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
        userInfo[@"peripheral"] = peripheral;
        userInfo[@"error"] = error;
        DDLogInfo(@"🔵: disconnect %@ %@",error?error.description:@"normally", peripheral);
        if (weakSelf.disconnectStateBlock) weakSelf.disconnectStateBlock(error);
        weakSelf.disconnectStateBlock = nil;
        [[NSNotificationCenter defaultCenter] postNotificationName:blePeripheralDisconnectNotification object:nil userInfo:userInfo];
        [weakSelf resetData];
    }];
    
    [_bluetooth setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
        for (CBCharacteristic *charc in service.characteristics) {
            if ([charc.UUID.UUIDString isEqualToString:weakSelf.readUUID]) {
                weakSelf.notifyCharc = charc;
                DDLogInfo(@"🔵: read characteristic:%@ properties:%zd - %@", charc.UUID.UUIDString,charc.properties, peripheral);
                [weakSelf.bluetooth notify:peripheral characteristic:charc block:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {
                    if (pthread_main_np()) {
                        [weakSelf notifyValueForCharacteristic:characteristics];
                    }else {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [weakSelf notifyValueForCharacteristic:characteristics];
                        });
                    }
                }];
            }
            if ([charc.UUID.UUIDString isEqualToString:weakSelf.writeUUID]) {
                DDLogInfo(@"🔵: write characteristic:%@ properties:%zd - %@", charc.UUID.UUIDString,charc.properties, peripheral);
                weakSelf.writeCharc = charc;
            }
        }
    }];
    ///写入成功回调
    [_bluetooth setBlockOnDidWriteValueForCharacteristic:^(CBCharacteristic *characteristic, NSError *error) {
        DDLogInfo(@"🔵: %@ did write %@",characteristic.UUID.UUIDString, error?[NSString stringWithFormat:@"failure:[%@]",error.description]:@"success");
    }];
    ///读取回调
    [_bluetooth setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {
        NSString *errorInfo = [NSString stringWithFormat:@"failure:[%@]",error.description];
        DDLogInfo(@"🔵: %@ did read %@",characteristic.UUID.UUIDString, error?errorInfo:@"success");
    }];
}

- (void)notifyValueForCharacteristic:(CBCharacteristic *)characteristic {
    if ([[characteristic value] length] == 0) return;
    if (![characteristic.UUID.UUIDString isEqualToString:self.readUUID]) return;
    NSData *data = [characteristic value];
    DDLogInfo(@"🔵: characteristic data: %@",data.vl_hex);
    if (data.length >= 4 && self.syncCode == data.vl_uint16) {
        [self resetPacketData];
        _packetDataLength = CFSwapInt16BigToHost([data vl_uint16At:2]);
        DDLogInfo(@"🔵: data length: %d",(int)_packetDataLength);
    }
    if (_packetDataLength > 2000) {
        NSLog(@"🔵: data length error: %d",(int)_packetDataLength);
        [self resetPacketData];
        return;
    }
    [self.packetData appendData:data];
   
    if (self.packetData.length > 4 && self.packetData.vl_uint16 == self.syncCode && self.packetData.length == (_packetDataLength + 4)) {
        DDLogInfo(@"🔵: 数据长度校验成功 == %@ == 长度:%zd",self.packetData.vl_hex,self.packetData.length);
        [self parseNotifyData];
    }else {
        DDLogInfo(@"🔵: 数据长度校验失败 == %@ == 实际长度:%zd, 预设长度:%hu",self.packetData.vl_hex,self.packetData.length, _packetDataLength);
    }
}

- (void)resetPacketData {
    _packetDataLength = 0;
    self.packetData.length = 0;
}

- (void)fulfillCommandResponse:(APBLEResponse *)response {
    APBLEVLinkCommandType command = response.command;
    NSData *data = response.data;
    BOOL isTimeout = response.isTimeout;
    NSLog(@"fulfill command:%04lX, data:%@, isTimeout:%d",(unsigned long)command,data?data.vl_hex:@"NULL",isTimeout);
    switch (command) {
        case APBLEVLinkCommandType_ConnnectState:
        {
            int8_t state = data.vl_int8;
            [[NSNotificationCenter defaultCenter] postNotificationName:blePeripheralConnectedNotification object:@(state)];
            if (self.connectStateBlock) {
                self.connectStateBlock(state);
                self.connectStateBlock = nil;
            }
            if (isTimeout) {
                [self fulfillCommandResponse:[APBLEResponse responseWithCommand:APBLEVLinkCommandType_RandomCode isTimeout:YES data:VLInt8Data(APBLEDeviceConnectStatus_Success)]];
            }
        }
            break;
        case APBLEVLinkCommandType_RandomCode:
        {
            if (self.shouldEncrypt && !isTimeout) self.randomCode = data.vl_uint32;
            if (self.encryptKey && !isTimeout) { ///< 随机码获取后，发送如果存在密钥就发送密钥校验
                [self sendCommand:APBLEVLinkCommandType_SecretCheck data:self.encryptKey];
            }else {
                DDLogInfo(@"🔵: ble not bind device yet");
            }
            self.isCommandRepared = !isTimeout;
            if (self.commandPrepareBlock) {
                self.commandPrepareBlock(!isTimeout);
                self.commandPrepareBlock = nil;
            }
        }
            break;
        case APBLEVLinkCommandType_SecretCheck:
        {
            if (data.vl_int16) { ///< 密钥校验失败，清除密钥缓存
                DDLogInfo(@"🔵: ble secret check failed");
                self.encryptKey = nil;
            }else {
                DDLogInfo(@"🔵: ble secret check success");
            }
        }
            break;
        case APBLEVLinkCommandType_SyncTime:
        {
            NSData *ts = VLUInt32Data([NSDate date].timeIntervalSince1970);
            NSData *tz = VLInt16Data([NSTimeZone systemTimeZone].secondsFromGMT/60);
            NSMutableData *dat = [NSMutableData dataWithData:ts];
            [dat appendData:tz];
            [self sendCommand:APBLEVLinkCommandType_SyncTime data:[dat copy]];
        }
            break;
        default:
            break;
    }
    /// 处理block回调
    if (command != APBLEVLinkCommandType_ConnnectState && self.commandResponses[@(command)]) {
        ((APBLECommandResponseBlock)self.commandResponses[@(command)])(response);
        self.commandResponses[@(command)] = nil;
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:bleReceivedCommandDataNotification object:response];
    if (self.commandTimers[@(command)]) {
        [self.commandTimers[@(command)] invalidate];
        self.commandTimers[@(command)] = nil;
    }
    if (isTimeout && command == APBLEVLinkCommandType_ConnnectState) {
        [self disconnect:self.device.peripheral];
    }
}

///< 解析数据
- (void)parseNotifyData {
    APBLEResponse *resp = [self unpackData];
    if (nil != resp) {
        [self fulfillCommandResponse:resp];
    }
}

- (void)stopDiscover {
    [_bluetooth cancelScan];
}


- (void)startDiscover:(NSArray<NSString *> *)prefixes {
    self.prefixes = prefixes;
    [_bluetooth cancelAllPeripheralsConnection];
    _bluetooth.scanForPeripherals().begin();
}

- (void)connectPeripheral:(CBPeripheral *)peripheral { // 连接外设
    [_bluetooth cancelScan];
    self.isConnecting = YES;
    _bluetooth.having(peripheral).connectToPeripherals().discoverServices().discoverCharacteristics().begin();
}

- (void)connectWithDevice:(APBLEDevice *)device config:(NSDictionary *)config encryptKey:(NSData *)encryptKey timeout:(double)timeout connectStateBlock:(void (^)(APBLEDeviceConnectStatus))connectStateBlock {
    self.device = device;
    self.encryptKey = encryptKey;
    self.config = config;
    self.isConnecting = YES;
    self.connectStateBlock = connectStateBlock;
    [self connectPeripheral:device.peripheral];
    if (timeout > 0) {
        [self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] invalidate];
        self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] = [VLTimer timerWithTimeInterval:timeout target:self selector:@selector(timerAction:) repeats:NO];
    }
}

- (void)connectWithDevice:(APBLEDevice *)device config:(NSDictionary *)config encryptKey:(NSData *)encryptKey timeout:(double)timeout commandPrepareBlock:(void (^)(BOOL))commandPrepareBlock {
    self.device = device;
    self.encryptKey = encryptKey;
    self.config = config;
    self.isConnecting = YES;
    self.commandPrepareBlock  = commandPrepareBlock;
    [self connectPeripheral:device.peripheral];
    if (timeout > 0) {
        [self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] invalidate];
        self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] = [VLTimer timerWithTimeInterval:timeout target:self selector:@selector(timerAction:) repeats:NO];
    }
}


- (void)connectWithName:(NSString *)bleName config:(NSDictionary *)config encryptKey:(NSData *)encryptKey timeout:(double)timeout connectStateBlock:(void (^)(APBLEDeviceConnectStatus))connectStateBlock {
    if ([bleName length]) {
        self.bleName = bleName;
        self.encryptKey = encryptKey;
        self.config = config;
        self.isConnecting = YES;
        self.connectStateBlock = connectStateBlock;
        [self startDiscover:@[config[@"prefix"]]];
        if (timeout > 0) {
            [self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] invalidate];
            self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] = [VLTimer timerWithTimeInterval:timeout target:self selector:@selector(timerAction:) repeats:NO];
        }
    }
}

- (void)connectWithName:(NSString *)bleName config:(NSDictionary *)config encryptKey:(NSData *)encryptKey timeout:(double)timeout commandPrepareBlock:(void (^)(BOOL))commandPrepareBlock {
    if ([bleName length]) {
        self.bleName = bleName;
        self.encryptKey = encryptKey;
        self.config = config;
        self.isConnecting = YES;
        self.commandPrepareBlock  = commandPrepareBlock;
        [self startDiscover:@[config[@"prefix"]]];
        if (timeout > 0) {
            [self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] invalidate];
            self.commandTimers[@(APBLEVLinkCommandType_ConnnectState)] = [VLTimer timerWithTimeInterval:timeout target:self selector:@selector(timerAction:) repeats:NO];
        }
    }
}

- (void)resetData {
    [self.commandResponses removeAllObjects];
    [self.commandTimers removeAllObjects];
    self.randomCode = 0;
    self.device = nil;
    self.bleName = nil;
    self.encryptKey = nil;
    self.connectStateBlock = nil;
    self.disconnectStateBlock = nil;
    self.commandResponseBlock = nil;
    self.isCommandRepared = NO;
    self.isConnecting = NO;
    self.packetDataLength = 0;
    self.packetData.length = 0;
    self.config = nil;
}

- (void)disconnect {
    if (self.device && self.notifyCharc) {
        [_bluetooth cancelNotify:self.device.peripheral characteristic:self.notifyCharc];
    }
    [_bluetooth cancelScan];
    [_bluetooth cancelAllPeripheralsConnection];
    [self resetData];
}

- (void)disconnect:(CBPeripheral *)peripheral {
    [_bluetooth cancelScan];
    if (self.device && self.notifyCharc) {
        [_bluetooth cancelNotify:self.device.peripheral characteristic:self.notifyCharc];
        [_bluetooth cancelPeripheralConnection:peripheral];
    }
    [self resetData];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command {
    [self sendCommand:command data:nil seq:arc4random_uniform(pow(2, 10) - 1) timeout:0 completion:nil];
}
- (void)sendCommand:(APBLEVLinkCommandType)command timeout:(double)timeout {
    [self sendCommand:command data:nil seq:arc4random_uniform(pow(2, 10) - 1) timeout:timeout completion:nil];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command data:(nullable NSData *)data {
    [self sendCommand:command data:data seq:arc4random_uniform(pow(2, 10) - 1) timeout:0 completion:nil];
}
- (void)sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data timeout:(double)timeout {
    [self sendCommand:command data:data seq:arc4random_uniform(pow(2, 10) - 1) timeout:timeout completion:nil];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command seq:(uint16_t)seq {
    [self sendCommand:command data:nil seq:seq timeout:0 completion:nil];
}
- (void)sendCommand:(APBLEVLinkCommandType)command seq:(uint16_t)seq timeout:(double)timeout {
    [self sendCommand:command data:nil seq:seq timeout:timeout completion:nil];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data seq:(uint16_t)seq {
    [self sendCommand:command data:data seq:seq timeout:0 completion:nil];
}
- (void)sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data seq:(uint16_t)seq timeout:(double)timeout {
    [self sendCommand:command data:data seq:seq timeout:timeout completion:nil];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:nil seq:arc4random_uniform(pow(2, 10) - 1) timeout:0 completion:completion];
}
- (void)sendCommand:(APBLEVLinkCommandType)command timeout:(double)timeout completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:nil seq:arc4random_uniform(pow(2, 10) - 1) timeout:timeout completion:completion];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command data:(nullable NSData *)data completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:data seq:arc4random_uniform(pow(2, 10) - 1) timeout:0 completion:completion];
}
- (void)sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data timeout:(double)timeout completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:data seq:arc4random_uniform(pow(2, 10) - 1) timeout:timeout completion:completion];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command seq:(uint16_t)seq completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:nil seq:seq timeout:0 completion:completion];
}
- (void)sendCommand:(APBLEVLinkCommandType)command seq:(uint16_t)seq timeout:(double)timeout completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:nil seq:seq timeout:timeout completion:completion];
}

///**************************************************************************
- (void)sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data seq:(uint16_t)seq completion:(nullable APBLECommandResponseBlock)completion {
    [self sendCommand:command data:data seq:seq timeout:0 completion:completion];
}


- (void)_sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data seq:(uint16_t)seq {
    
    if (self.shouldEncrypt) { // 加密版本
        if (command != APBLEVLinkCommandType_RandomCode && self.randomCode == 0) { ///< 随机吗未获取到
            DDLogInfo(@"🔵: random code is nil while send %02lX",command);
            return;
        }
        if (command == APBLEVLinkCommandType_UnbindDevice) { ///< 解绑设备，密钥不能为空
            if (self.encryptKey == nil) {
                DDLogInfo(@"🔵: unbind device error due to nil encrypt key");
                return;
            } else {
                data = self.encryptKey;
            }
        }
    }
    _sendedCommand = command;
    NSMutableData *packedData = [NSMutableData dataWithData:[self packWithData:data?:[NSData data] cmd:command seq:seq]];
    if (self.device && self.writeCharc) {
        CBCharacteristicWriteType writeType = 1;
//        NSUInteger maxMTU = [self.peripheral maximumWriteValueLengthForType:writeType];
        NSUInteger maxMTU = 20;
//        CBCharacteristicWriteType writeType = APGlobal.BLEWriteWithResponse?CBCharacteristicWriteWithResponse:CBCharacteristicWriteWithoutResponse;
        while (packedData.length >= maxMTU) {
            NSData *pkt = [packedData subdataWithRange:NSMakeRange(0, maxMTU)];
            [self.device.peripheral writeValue:pkt forCharacteristic:self.writeCharc type:writeType];
            [packedData replaceBytesInRange:NSMakeRange(0, maxMTU) withBytes:NULL length:0];
        }
        //将剩余不足20长度的数据发过去
        if (packedData.length > 0) {
            [self.device.peripheral writeValue:packedData forCharacteristic:self.writeCharc type:writeType];
        }
    }
}

- (void)sendCommand:(APBLEVLinkCommandType)command data:(NSData *)data seq:(uint16_t)seq timeout:(double)timeout completion:(void (^)(APBLEResponse *response))completion {
    DDLogInfo(@"🔵: send command prepare:%@ --- %d",self.device,self.isConnected);
    if (self.device && self.isConnected) {
        self.commandResponses[@(command)] = completion;
        [self _sendCommand:command data:data seq:seq];
        if (timeout > 0) {
            [self.commandTimers[@(command)] invalidate];
            self.commandTimers[@(command)] = [VLTimer timerWithTimeInterval:timeout target:self selector:@selector(timerAction:) repeats:NO];
        }
    }
}

- (void)timerAction:(VLTimer *)timer {
    [self.commandTimers enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, VLTimer * _Nonnull obj, BOOL * _Nonnull stop) {
        if (obj == timer) {
            [self fulfillCommandResponse:[APBLEResponse responseWithCommand:[key unsignedIntegerValue] isTimeout:YES data:VLInt8Data(APBLEDeviceConnectStatus_Timeout)]];
            *stop = YES;
        }
    }];
}

///< 数据封装
- (NSData *)packWithData:(NSData *)data cmd:(uint16_t)cmd seq:(uint16_t)seq {
    NSData *syncData = VLUInt16Data(self.syncCode); ///< 同步码数据
    NSData *toData = VLUInt8Data(((cmd==0x9001||cmd==0x9010)?WFMD_ADDR:LKMD_ADDR)); ///< 数据接收方
    NSData *fromData = VLUInt8Data(APP_ADDR); ///< 数据发送方
    NSData *diffData = self.shouldEncrypt?VLUInt32Data(self.vl_diff_val):VLUInt8Data(self.vl_diff_val); ///< 协议类型或者随机码 D288:随机码
    uint16_t type = 1; ///< 消息类型
    uint16_t ver = 1; ///< 消息版本
    NSData *msgHData = VLUInt16Data(CFSwapInt16HostToBig(seq + (type << 10) + (ver << 12))); ///< 上面3个类型的共用体数据（两个字节）
    NSData *cmdData = VLUInt16Data(CFSwapInt16HostToBig(cmd)); ///< 命令数据
    NSData *msgData = VLDataArray(@[msgHData,cmdData,data]);
    NSData *encryptData = self.needEncrypt?[msgData vl_aes128EncryptWithKey:self.encryptKey]:msgData;
    uint16_t checksum = 0; ///< 校验和数据
    ///< 长度数据
    uint16_t len = fromData.length + toData.length + diffData.length + encryptData.length + sizeof(checksum);
    NSData *lenData = VLUInt16Data(CFSwapInt16HostToBig(len));
    NSData *caculateData = VLDataArray(@[lenData,toData,fromData,diffData,encryptData]); ///< 计算校验和的数据
    checksum = CRC16_XMODEM((uint8_t *)caculateData.bytes, (uint32_t)caculateData.length);
    NSData *checksumData = VLUInt16Data(CFSwapInt16HostToBig(checksum));
    DDLogInfo(@"🔵: ➡️ 开始发送数据");
    NSLog(@"🔵: ➡️ 同步码: %@",syncData.vl_hex);
    NSLog(@"🔵: ➡️ 数据长度: %d[%@]",len,lenData.vl_hex);
    NSLog(@"🔵: ➡️ 接收端: %@",toData.vl_hex);
    NSLog(@"🔵: ➡️ 发送端: %@",fromData.vl_hex);
    NSLog(@"🔵: ➡️ %@: %@",(self.shouldEncrypt?@"随机码":@"协议类型"),diffData.vl_hex);
    NSLog(@"🔵: ➡️ 消息版本: %d",ver);
    NSLog(@"🔵: ➡️ 消息类型: %d",type);
    NSLog(@"🔵: ➡️ 消息序号: %d",seq);
    NSLog(@"🔵: ➡️ 消息头部[版本、类型、序号的共用体数据]: %@",msgHData.vl_hex);
    DDLogInfo(@"🔵: ➡️ 指令: %@",cmdData.vl_hex);
    DDLogInfo(@"🔵: ➡️ 消息内容: %@[%ld]",(data.length?data.vl_hex:@"NULL"),data.length);
    if (self.needEncrypt) {
        NSLog(@"🔵: ➡️ 加密前内容: %@",msgData.vl_hex);
        NSLog(@"🔵: ➡️ 加密密钥: %@",self.encryptKey.vl_hex);
        NSLog(@"🔵: ➡️ 加密后内容: %@",encryptData.vl_hex);
    }
    NSLog(@"🔵: ➡️ 数据校验和: %02X",CFSwapInt16HostToBig(checksum));
    NSData *mData = VLDataArray(@[syncData,caculateData,checksumData]);
    DDLogInfo(@"🔵: ➡️ 发送指令:0x%04X 数据:%@",cmd ,mData.vl_hex);
    NSLog(@"🔵: ===================================================> ");
    return mData;
}

///< 数据解封装
///< 0A000049FF0000000000000087150755C855F6FFB0ACDD709EBD6EE97AB20327A9A1191E97A0A8342336938157D8F74DFDE21A06F63E82C5E78E816B
///< 0A000049FF000000000000000001FC6179CE9829312E30302E3030000000312E30302E333000000053453630000000000000010002(Black view)
///<
- (nullable APBLEResponse *)unpackData {
    NSMutableData *packetData = [NSMutableData dataWithData:self.packetData];
    APBLEResponse *resp = [APBLEResponse new];
    resp.response = [packetData copy];
    [self resetPacketData];
    DDLogInfo(@"🔵: ⬅️ 开始解析数据:%@[%ld]",packetData.vl_hex,packetData.length);
    ///< 接受的数据部分，len、cmd、check_sum是大端数据
    resp.syncCode = VLRemoveData(packetData, 2); ///< 同步码数据
    NSData *lenData = VLRemoveData(packetData, 2); ///< 数据长度
    resp.dataLength = CFSwapInt16BigToHost(lenData.vl_int16);
    resp.destData = VLRemoveData(packetData, 1); ///< 消息接收端
    resp.srcData = VLRemoveData(packetData, 1); ///< 消息发送端
    resp.typeData = VLRemoveData(packetData, self.shouldEncrypt?4:1); ///< 协议类型或者随机码
    NSData *encData = VLRemoveData(packetData, packetData.length-2); ///< 消息体数据（加密会用到）
    resp.encryptKey = [self.encryptKey copy];
    ///< 解密后的消息体数据[不存在加密时，不变]
    BOOL shouldDecrypt = self.needEncrypt&&self.sendedCommand != APBLEVLinkCommandType_UnbindDevice;
    NSMutableData *decryptData = [NSMutableData dataWithData:shouldDecrypt?[encData vl_aes128DecryptWithKey:self.encryptKey]:encData];
    NSData *decryptData_copy = [decryptData copy];
    resp.msgData = VLRemoveData(decryptData, 2); ///< 消息头部数据（包含版本，类型、序号）
    uint16_t msgH = CFSwapInt16BigToHost(encData.vl_uint16);
    resp.msgVersion = (msgH & ver_mask) >> 12;
    resp.msgType = (msgH & type_mask) >> 10;
    resp.msgSequence = msgH & seq_mask;
    NSData *cmdData = VLRemoveData(decryptData, 2); ///< 指令数据
    resp.command = CFSwapInt16BigToHost(cmdData.vl_uint16);
    resp.data = VLRemoveData(decryptData, decryptData.length); ///< 真正消息数据部分
    NSData *checksumData = VLRemoveData(packetData, 2); ///< 校验和数据
    resp.checkSum = CFSwapInt16BigToHost(checksumData.vl_uint16);
    NSLog(@"🔵: ⬅️ 数据长度: %zd",packetData.length);
    NSLog(@"🔵: ⬅️ 同步码: %@",resp.syncCode.vl_hex);
    NSLog(@"🔵: ⬅️ 数据长度: %ld[%@]",resp.dataLength,lenData.vl_hex);
    NSLog(@"🔵: ⬅️ 接收端: %@",resp.destData.vl_hex);
    NSLog(@"🔵: ⬅️ 发送端: %@",resp.srcData.vl_hex);
    NSLog(@"🔵: ⬅️ %@: %@",(self.shouldEncrypt?@"随机码":@"协议类型"),resp.typeData.vl_hex);
    NSLog(@"🔵: ⬅️ 消息版本: %ld",resp.msgVersion);
    NSLog(@"🔵: ⬅️ 消息类型: %ld",resp.msgType);
    NSLog(@"🔵: ⬅️ 消息序号: %ld",resp.msgSequence);
    NSLog(@"🔵: ⬅️ 消息头部[版本、类型、序号的共用体数据]: %@",resp.msgData.vl_hex);
    DDLogInfo(@"🔵: ⬅️ 指令: %@",cmdData.vl_hex);
    if (shouldDecrypt) {
        DDLogInfo(@"🔵: ⬅️ 解密前内容: %@",encData.vl_hex);
        NSLog(@"🔵: ⬅️ 解密密钥: %@",self.encryptKey.vl_hex);
        DDLogInfo(@"🔵: ⬅️ 解密后内容: %@",decryptData_copy.vl_hex);
    }
    DDLogInfo(@"🔵: ⬅️ 消息内容: %@[%zd]",(resp.data.length?resp.data.vl_hex:@"NULL"),resp.data.length);
    NSLog(@"🔵: ⬅️ 数据校验和: %02X",(uint16_t)resp.checkSum);
    NSLog(@"🔵: <=================================================== ");
    if (resp.syncCode.vl_uint16 != self.syncCode) {
        DDLogError(@"🔵: 同步码错误:%02X",resp.syncCode.vl_uint16);
        return nil;
    }
    NSData *caculateData = VLDataArray(@[lenData,resp.destData,resp.srcData,resp.typeData,encData]); ///< 计算校验和的数据
    uint16_t checkSum = CRC16_XMODEM((uint8_t *)caculateData.bytes, (uint32_t)caculateData.length);
    if (resp.checkSum != checkSum) {
        DDLogError(@"🔵: 校验错误:%02X - %02X",(uint16_t)resp.checkSum,checkSum);
        return nil;
    }
    if (resp.data.length == 0) {
        resp.data = VLUInt8Data(0);
    }
    return resp;
}

NS_INLINE uint16_t CRC16_XMODEM(uint8_t *data, uint16_t len) {
    uint16_t wCRCin = 0x0000;
    uint16_t wCPoly = 0x1021;
    for (int i = 0; i < len; i++) {
        for (int j = 0; j < 8; j++) {
            bool bit = (data[i] >> (7 - j) & 1) == 1;
            bool c15 = (wCRCin >> 15 & 1) == 1;
            wCRCin <<= 1;
            if (c15 != bit) {
                wCRCin ^= wCPoly;
            }
        }
    }
    wCRCin &= 0xffff;
    wCRCin ^= 0x0000;
    return wCRCin;
}

@end
